package com.xanthreal.gvalid;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

public class ToDomainValidationEngine extends AbstractValidationEngine implements ValidationEngine {

	private AnnotationAdapterFactory annotationAdapterFactory;

	private ValidatorFactory validatorFactory;

	public ToDomainValidationEngine(AnnotationAdapterFactory annotationAdapterFactory, ValidatorFactory validatorFactory) {
		this.annotationAdapterFactory = annotationAdapterFactory;
		this.validatorFactory = validatorFactory;
	}

	public void validate(Method method, Object[] arguments) {
		Validator validator = validatorFactory.getValidator();
		Annotation[][] parameterAnnotations = method.getParameterAnnotations();
		Set<ConstraintViolation<Object>> violations = new HashSet<ConstraintViolation<Object>>();

		for (int i = 0; i < arguments.length; i++) {
			Validated validatedAnnotation;
			Annotation[] annotationOnParameterObjectArray = parameterAnnotations[i];
			if (annotationOnParameterObjectArray.length < 1) {
				continue;
			}
			final Object argumentToValidate = arguments[i];
			if (argumentToValidate == null) {
				throw new ConstraintViolationException("At least one of validated parameters is null.", null);
			}

			if ((validatedAnnotation = getValidAnnotation(annotationOnParameterObjectArray)) == null) {
				Annotation[] allConstraintedAnnotations = getAllConstraintedAnnotations(annotationOnParameterObjectArray);
				Object annotationAdapter = annotationAdapterFactory.createAnnotationAdapterForObject(
						method.getParameterTypes()[i], argumentToValidate, allConstraintedAnnotations);
				violations.addAll(validator.validate(annotationAdapter));
				continue;
			}
			Class<?>[] validationGroups = validatedAnnotation.groups();
			violations.addAll(validator.validate(arguments[i], validationGroups));
		}

		if (violations.size() > 0) {
			throw new ConstraintViolationException(violations);
		}
	}

}
